home *** CD-ROM | disk | FTP | other *** search
- /********************************************************************************
-
- TCPClose
-
- this snippet shows how to close a connection gracefully without causing errors or forcing
- aborts on either the local or remote end.
-
- note: don't run this from the think debugger, as it doesn't like getting invoked at
- interrupt time via a DebugStr()...
-
- Steve Falkenburg
- MacDTS
- 11/16/92
-
- ********************************************************************************/
-
-
- #include <MacTCPCommonTypes.h>
- #include <TCPPB.h>
-
- // constants
-
- #define kTimeout 30
- #define kFTPServerIP 0x5a85342c // change this to your ftp host !!
- #define kFTPPort 21
-
- // globals
-
- short gDrvrRef;
- Boolean gTerminated;
-
- // prototypes
-
- void main(void);
- OSErr MakeStream(StreamPtr *stream);
- OSErr DoFTPConnStuff(StreamPtr stream);
- OSErr OpenConnection(StreamPtr stream,long address,short port);
- OSErr CloseConnection(StreamPtr stream);
- OSErr ReceiveAndJunkData(StreamPtr stream);
- pascal void ASR(
- StreamPtr tcpStream,
- unsigned short eventCode,
- Ptr userDataPtr,
- unsigned short terminReason,
- struct ICMPReport *icmpMsg);
-
-
- // main program...
- //
- void main(void)
- {
- OSErr err;
- StreamPtr conn;
-
- err = OpenDriver("\p.IPP",&gDrvrRef); // open TCP driver
- if (err!=noErr) {
- DebugStr("\pMacTCP not installed");
- return;
- }
-
- err = MakeStream(&conn); // create our connection stream
- if (err!=noErr) {
- DebugStr("\pcouldn't make stream");
- return;
- }
-
- err = OpenConnection(conn,kFTPServerIP,21); // open a connection (change IP #)
- if (err!=noErr) {
- CloseConnection(conn);
- DebugStr("\pConnection not opened");
- return;
- }
-
- err = DoFTPConnStuff(conn); // do a short FTP session
- if (err!=noErr)
- DebugStr("\pFTP server not responding");
-
- err = CloseConnection(conn); // close the connection and stream
- }
-
-
- // opens a TCP stream and returns the stream ptr. we use an 8k stream buffer
- //
- OSErr MakeStream(StreamPtr *stream)
- {
- OSErr err;
- TCPiopb pBlock;
- Ptr connBuffer;
- long connBufferLen;
-
- connBufferLen = 8192;
- connBuffer = NewPtr(connBufferLen);
- if (MemError()!=noErr)
- return MemError();
-
- pBlock.ioCRefNum = gDrvrRef;
- pBlock.csCode = TCPCreate;
- pBlock.csParam.create.rcvBuff = connBuffer;
- pBlock.csParam.create.rcvBuffLen = connBufferLen;
- pBlock.csParam.create.notifyProc = ASR;
- err = PBControl(&pBlock,false);
-
- *stream = pBlock.tcpStream;
-
- if (err!=noErr)
- DisposPtr(connBuffer);
-
- return err;
- }
-
-
- // opens the connection to the ip # and tcp port passed in
- //
-
- OSErr OpenConnection(StreamPtr stream,long address,short port)
- {
- OSErr err;
- TCPiopb pBlock;
-
- gTerminated = false;
-
- pBlock.ioCRefNum = gDrvrRef;
- pBlock.csCode = TCPActiveOpen;
- pBlock.tcpStream = stream;
- pBlock.csParam.open.ulpTimeoutValue = kTimeout;
- pBlock.csParam.open.ulpTimeoutAction = 1;
- pBlock.csParam.open.validityFlags = 0xC0;
- pBlock.csParam.open.commandTimeoutValue = kTimeout;
- pBlock.csParam.open.remoteHost = address;
- pBlock.csParam.open.remotePort = port;
- pBlock.csParam.open.localPort = 0;
- pBlock.csParam.open.tosFlags = 0;
- pBlock.csParam.open.precedence = 0;
- pBlock.csParam.open.dontFrag = 0;
- pBlock.csParam.open.timeToLive = 0;
- pBlock.csParam.open.security = 0;
- pBlock.csParam.open.optionCnt = 0;
- err = PBControl((ParmBlkPtr)&pBlock,false);
-
- return err;
- }
-
-
- // does a short FTP session, consisting of receiving the welcome message,
- // issuing a "QUIT" command, and receiving the response
- //
- OSErr DoFTPConnStuff(StreamPtr stream)
- {
- OSErr err;
- TCPiopb pBlock;
- wdsEntry wds[2];
-
- wds[0].length = 6;
- wds[0].ptr = "QUIT\r\n";
- wds[1].length = 0;
-
- err = ReceiveAndJunkData(stream);
- if (err!=noErr)
- DebugStr("\precverror");
-
- pBlock.ioCRefNum = gDrvrRef;
- pBlock.csCode = TCPSend;
- pBlock.tcpStream = stream;
- pBlock.csParam.send.ulpTimeoutValue = kTimeout;
- pBlock.csParam.send.ulpTimeoutAction = 1;
- pBlock.csParam.send.validityFlags = 0xC0;
- pBlock.csParam.send.pushFlag = false;
- pBlock.csParam.send.urgentFlag = true;
- pBlock.csParam.send.wdsPtr = (Ptr)wds;
- err = PBControl((ParmBlkPtr)&pBlock,false);
- if (err!=noErr)
- DebugStr("\perrsend");
-
- err = ReceiveAndJunkData(stream);
- if (err!=noErr)
- DebugStr("\precverror");
-
- return err;
- }
-
-
- // close the connection gracefully. this involves issuing the TCPClose, receiving data
- // until we get an error (the remote end is closing) and issue a TCPRelease to get rid of
- // the stream.
- //
- OSErr CloseConnection(StreamPtr stream)
- {
- OSErr err;
- TCPiopb pBlock;
-
- pBlock.ioCRefNum = gDrvrRef;
- pBlock.csCode = TCPClose;
- pBlock.tcpStream = stream;
- pBlock.csParam.close.ulpTimeoutValue = kTimeout;
- pBlock.csParam.close.validityFlags = 0xC0;
- pBlock.csParam.close.ulpTimeoutAction = 1;
- err = PBControl((ParmBlkPtr)&pBlock,false);
-
- // receive data until the connection closes
-
- do {
- err = ReceiveAndJunkData(stream);
- } while (!gTerminated);
-
- pBlock.ioCRefNum = gDrvrRef;
- pBlock.csCode = TCPRelease;
- pBlock.tcpStream = stream;
- err = PBControl((ParmBlkPtr)&pBlock,false);
-
- if (err!=noErr)
- return err;
-
- DisposPtr(pBlock.csParam.create.rcvBuff);
- return err;
- }
-
-
- // receive a block of data from the remote connection, but don't even return what the data
- // is. we're using the no-copy form of the receive command for simplicity.
- //
- OSErr ReceiveAndJunkData(StreamPtr stream)
- {
- OSErr err;
- TCPiopb pBlock;
- rdsEntry rds[3];
-
- // set up our 2-part rds
-
- rds[0].length = 0;
- rds[0].ptr = nil;
- rds[1].length = 0;
- rds[1].ptr = nil;
- rds[2].length = 0;
- rds[2].ptr = nil;
-
- // issue the receive
-
- pBlock.ioCRefNum = gDrvrRef;
- pBlock.csCode = TCPNoCopyRcv;
- pBlock.tcpStream = stream;
- pBlock.csParam.receive.commandTimeoutValue = kTimeout;
- pBlock.csParam.receive.rdsPtr = (Ptr)rds;
- pBlock.csParam.receive.rdsLength = 2;
- err = PBControl((ParmBlkPtr)&pBlock,false);
-
- // return the buffer
-
- if (err==noErr) {
- pBlock.csCode = TCPRcvBfrReturn;
- err = PBControl((ParmBlkPtr)&pBlock,false);
- }
-
- return err;
- }
-
-
- // our asynchronous notification routine. this gets called several times, but we only
- // look at the connection termination messages. we do this to determine if the close
- // was graceful, or if either we or the remote end had to abort.
- //
- pascal void ASR(
- StreamPtr tcpStream,
- unsigned short eventCode,
- Ptr userDataPtr,
- unsigned short terminReason,
- struct ICMPReport *icmpMsg)
- {
- if (eventCode==TCPTerminate) {
- gTerminated = true;
- switch (terminReason) {
- case TCPULPClose:
- DebugStr("\pgraceful close on both ends"); // this is the one you want
- break;
- case TCPRemoteAbort:
- DebugStr("\premote connection forced abort");
- break;
- case TCPULPTimeoutTerminate:
- DebugStr("\pULP timeout reached so connection dropped");
- break;
- case TCPULPAbort:
- DebugStr("\pTCPRelease issued before remote end closed");
- break;
- default:
- DebugStr("\pconnection terminated non-gracefully");
- break;
- }
- }
- }
-